## Инициализатор __init__ и финализатор __del__

После определения класса `Point` создаются два объекта класса Point - `p1 и p2`. 

Для создания объекта применяется специальная функция - конструктор, которая называется по имени класса и которая возвращает объект класса. 

То есть в данном случае вызов `Point()` представляет вызов конструктора. 

Каждый класс по умолчанию имеет конструктор без параметров:

```python
p1 = Point()
```

### Конструктор

Конструктор – специальные метод, который (1) запускается автоматически, (2) запускается при создании объекта
Итак, для создания объекта класса  класса используется конструктор. 

Так, выше когда мы создавали объекты класса `Point`, мы использовали конструктор по умолчанию, 
который не принимает параметров и который неявно имеют все классы. 

Однако мы можем явным образом определить в классах конструктор с помощью специального метода, который называется `__init__()` (по два прочерка с каждой стороны).


Иногда можно встретить вместо конструктор слова инициализатор, а даже инстанс (в java).

Т.о. создается объект, вызывается его конструктор, записывается в память (как правило это обычная переменная). 

Это обычная переменная, все объекты в пайтоне – ссылочного типа. 

поговорим о начальной инициализации объектов в `Python` в момент их создания и финализации при их удалении.

В каждом классе языка Python есть набор предопределенных «магических» методов. Да, это такое общепринятое название. Магические методы начинаются и заканчиваются двумя подчеркиваниями:

`__ имя метода__`

В частности существуют два таких метода:

```python
__init__(self) –  инициализатор объекта класса
__del__(self) – финализатор класса
```
Первый вызывается сразу после создания экземпляра класса, а второй – перед непосредственным его удалением. 

Давайте посмотрим, как они работают и зачем нужны.

Я вернусь к примеру из прошлого занятия, где мы с вами определили класс с двумя свойствами `color` и `circle` и двумя методами `set_coords` и `get_coords`:

```python
class Point:
    color = 'red'
    circle = 2
 
    def set_coords(self, x, y):
        self.x = x
        self.y = y
 
    def get_coords(self):
        return (self.x, self.y)
```

Использовать на практике такой класс не очень удобно, так как после создания объекта:

```python
pt = Point()
```
координат `x`, `y` в нем никаких не будет. Дополнительно для этого нужно еще вызывать метод:

```python
pt.set_coords(1, 2)
```


Было бы хорошо сделать эти действия сразу в момент создания экземпляра класса. 

И поможет нам в этом магический метод `__init__`. Давайте сначала мы его пропишем в самом простом виде:


```python
class Point:
    color = 'red'
    circle = 2
 
    def __init__(self):
        print("вызов __init__")
        self.x = 0
        self.y = 0
    ...
```

Здесь первый параметр `self` является ссылкой на созданный экземпляр класса, через который мы создаем в этом новом объекте локальные свойства `x, y`. 
В результате, создавая экземпляр класса:

```python
pt = Point()
```
мы увидим в консоли, что, во-первых, произошел вызов этого метода и, во-вторых, в объекте `pt` были созданы два локальных свойства `x, y` с нулевыми значениями:

```python
print(pt.__dict__)
```
Детально это работает, следующим образом.

Вначале происходит создание объекта в памяти устройства. 

Непосредственно перед его созданием вызывается магический метод `__new__` (О нем мы еще будем говорить). 

Затем, после успешного создания объекта, вызывается магический метод `__init__` для начальной инициализации созданного объекта. 
В результате, у нас появляются два локальных атрибута `x и y`.

Давайте теперь сделаем так, чтобы в момент создания экземпляра класса мы могли сразу указывать значения координат точки. 
Для этого в инициализаторе можно прописывать дополнительные параметры:

```python
    def __init__(self, a, b):
        self.x = a
        self.y = b
```
И при создании объекта с таким инициализатором мы уже должны в круглых скобках передавать эти два аргумента:


```python
pt = Point(1, 2)
```

Если этого не сделать, то возникнет ошибка и объект создан не будет.

Обратите внимание, я специально в качестве дополнительных параметров указал имена `a и b`. 

При этом, в самом объекте создаются локальные свойства с именами `x и y`. То есть, то, что мы прописываем после `self`, то и является именем нового атрибута. 

Конечно, на практике, обычно, имена параметров в инициализаторе совпадают с именами создаваемых свойств и лучше записать инициализатор в таком виде:

```python
    def __init__(self, x, y):
        self.x = x
        self.y = y
```

Также всегда следует помнить, что метод `__init__` - это обычная функция, поэтому мы в качестве дополнительных параметров можем указывать и фактические и формальные параметры.

Например, так:

```python
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y
```

Тогда, при создании объектов мы можем вообще не указывать аргументы:

```python
pt = Point()
```
Либо указать один или два:

```python
pt = Point(10)
pt = Point(10, 20)
```

То есть, здесь все ровно так, как и с обычными функциями.

### Финализатор объекта класса

Я, надеюсь, вы теперь хорошо себе представляете, как работает инициализатор объектов класса и зачем он нужен.

Второй аналогичный магический метод `__del__` автоматически вызывается непосредственно перед уничтожением экземпляра класса.

Он называется финализатор.

Давайте пропишем такой магический метод в классе Point, следующим образом:

```python
    def __del__(self):
        print("Удаление экземпляра: "+ str(self))
```

Здесь по-прежнему `self` – это ссылка на экземпляр класса, который будет удален.

После запуска программы видим, что объект в конце программы действительно был удален и вызван метод `__del__`. 

Но, здесь у вас может возникнуть вопрос: а когда и в какой момент вообще происходит удаление объектов. 

Мы же в программе это явно нигде не указываем? 

Работает все достаточно просто. 

Интерпретатор языка `Python` имеет, так называемый, сборщик мусора. 

Это алгоритм, который отслеживает объекты и как только они становятся ненужными, удаляет их. 

Но как он определяет нужный объект или ненужный?

Все очень просто.

Пока на какой-либо объект ведет хотя бы одна внешняя ссылка, то он считается используемым и сохраняется в памяти.

Как только все внешние ссылки пропадают, то сборщик мусора его уничтожает (освобождает память, которую он занимал).

Итак, из этого занятия вы должны знать, что такое магический метод и как работают инициализатор объекта и его финализатор.

А также в какой момент и когда они вызываются.

**Экземпляры**

Представляют конкретные элементы в предметной области программы.

Атрибуты экземпляров хранят данные, которые варьируются для каждого отдельного объекта (скажем, номер карточки социального страхования сотрудника).

```
С точки зрения деревьев поиска экземпляр наследует атрибуты от своего класса, а класс наследует атрибуты от всех классов выше в дереве.
```


- Основное отличие между классами и экземплярами состоит в том, что классы представляют собой своего рода фабрики ддя генерирования экземпляров.

- Отличие между классами и модулями — мы можем иметь только один экземпляр отдельного модуля в памяти (именно потому модуль приходится перезагружать, чтобы получить его новый код), но для классов допускается создавать столько экземпляров, сколько нужно.